<?php

namespace Core\Config;

require "Ref.php"; // TODO: cleanup -- needed by config to work before autoloaders are registered

class Container {

    private $configFilePath;
    private $configuration;
    private $objects = array();

    public function __construct($configFilePath) {
        $this->configFilePath = $configFilePath;
    }

    public function start() {
        $this->configuration = (require $this->configFilePath);

        // register autoloaders first
        foreach ($this->configuration["__autoloaders"] as $callback) {
            spl_autoload_register($callback);
        }

        // execute all postprocessors
        foreach ($this->configuration["__postprocessors"] as $postprocessorName) {
            $postprocessor = $this->getObject($postprocessorName);
            $postprocessor->execute();
        }
    }

    public function end() {
        
    }

    public function materializeReferences(&$object) {
        foreach ($object as &$propertyValue) {
            if ($this->isReference($propertyValue)) {
                $propertyValue = $this->getObject($propertyValue->getObjectName());
            }
        }
    }
    
    public function getObject($objectName) {
        if (array_key_exists($objectName, $this->objects)) {
            return $this->objects[$objectName];
        }

        $objectConfig = $this->configuration[$objectName];

        $classname = $objectConfig["class"];
        $reflectionClass = new \ReflectionClass($classname);

        if (array_key_exists("constructorArgs", $objectConfig)) {
            $object = $reflectionClass->newInstanceArgs($objectConfig["constructorArgs"]);
        } else {
            $object = new $classname();
        }

        $this->objects[$objectName] = $object;

        if (array_key_exists("properties", $objectConfig)) {
            $this->configure($object, $objectConfig["properties"]);
        }

        // container aware objects
        if (array_key_exists("containerAware", $objectConfig) && $objectConfig["containerAware"]) {
            $method = new \ReflectionMethod($object, "setContainer");
            $method->invoke($object, $this);
        }

        if (array_key_exists("initMethod", $objectConfig)) {
            $method = new \ReflectionMethod($object, $objectConfig["initMethod"]);
            $method->invoke($object);
        }

        return $object;
    }

    private function configure($object, $propertiesArray) {
        if (is_null($propertiesArray)) {
            return;
        }

        foreach ($propertiesArray as $propertyName => $propertyValue) {
            $methodName = "set" . ucfirst($propertyName);
            $method = new \ReflectionMethod($object, $methodName);

            $value = $propertyValue;
            if ($this->isReference($propertyValue)) {
                $value = $this->getObject($propertyValue->getObjectName());
            }

            $method->invoke($object, $value);
        }
    }
    
    private function isReference($object) {
        return gettype($object) == "object" && get_class($object) == 'Core\Config\Ref';
    }

};